package com.treetao.union.core.id;

import org.springframework.data.keyvalue.core.IdentifierGenerator;
import org.springframework.data.util.TypeInformation;

/**
 * Snowflake ID生成器
 * 
 * @author devm
 *
 */
public class SnowflakeIdGenerator implements IdentifierGenerator {

	/**
	 * 开始时间截
	 */
	private static final long TWEPOCH = 1587576380089L;

	/**
	 * 机器id所占的位数
	 */
	private static final long WORKER_ID_BITS = 5L;

	/**
	 * 数据标识id所占的位数
	 */
	private static final long DATA_CENTER_ID_BITS = 5L;

	/**
	 * 支持的最大机器id，结果是31 (这个移位算法可以很快的计算出几位二进制数所能表示的最大十进制数)
	 */
	private static final long MAX_WORKER_ID = -1L ^ (-1L << WORKER_ID_BITS);

	/**
	 * 支持的最大数据标识id，结果是31
	 */
	private static final long MAX_DATA_CENTER_ID = -1L ^ (-1L << DATA_CENTER_ID_BITS);

	/**
	 * 序列在id中占的位数
	 */
	private static final long SEQUENCE_BITS = 12L;

	/**
	 * 机器ID向左移12位
	 */
	private static final long WORKER_ID_SHIFT = SEQUENCE_BITS;

	/**
	 * 数据标识id向左移17位(12+5)
	 */
	private static final long DATA_CENTER_ID_SHIFT = SEQUENCE_BITS + WORKER_ID_BITS;

	/**
	 * 时间截向左移22位(5+5+12)
	 */
	private static final long TIMESTAMP_LEFT_SHIFT = SEQUENCE_BITS + WORKER_ID_BITS + DATA_CENTER_ID_BITS;

	/**
	 * 生成序列的掩码，这里为4095 (0b111111111111=0xfff=4095)
	 */
	private static final long SEQUENCE_MASK = -1L ^ (-1L << SEQUENCE_BITS);

	/**
	 * 工作机器ID(0~31)
	 */
	private static long workerId = 1;

	/**
	 * 数据中心ID(0~31)
	 */
	private static long datacenterId = 1;

	/**
	 * 毫秒内序列(0~4095)
	 */
	private static long sequence = 0L;

	/**
	 * 上次生成ID的时间截
	 */
	private static long lastTimestamp = -1L;

	public SnowflakeIdGenerator() {
	}

	/**
	 * 获得下一个ID (该方法是线程安全的)
	 *
	 */
	public static synchronized long nextId() {
		long timestamp = timeGen();

		// 如果当前时间小于上一次ID生成的时间戳，说明系统时钟回退过这个时候应当抛出异常
		if (timestamp < lastTimestamp) {
			throw new RuntimeException(String.format(
					"Clock moved backwards.  Refusing to generate id for %d milliseconds", lastTimestamp - timestamp));
		}

		// 如果是同一时间生成的，则进行毫秒内序列
		if (lastTimestamp == timestamp) {
			sequence = (sequence + 1) & SEQUENCE_MASK;
			// 毫秒内序列溢出
			if (sequence == 0) {
				// 阻塞到下一个毫秒,获得新的时间戳
				timestamp = tilNextMillis(lastTimestamp);
			}
		}
		// 时间戳改变，毫秒内序列重置
		else {
			sequence = 0L;
		}

		// 上次生成ID的时间截
		lastTimestamp = timestamp;

		// 移位并通过或运算拼到一起组成64位的ID
		return ((timestamp - TWEPOCH) << TIMESTAMP_LEFT_SHIFT) | (datacenterId << DATA_CENTER_ID_SHIFT)
				| (workerId << WORKER_ID_SHIFT) | sequence;
	}

	/**
	 * 阻塞到下一个毫秒，直到获得新的时间戳
	 *
	 * @param lastTimestamp 上次生成ID的时间截
	 * @return 当前时间戳
	 */
	protected static long tilNextMillis(long lastTimestamp) {
		long timestamp = timeGen();
		while (timestamp <= lastTimestamp) {
			timestamp = timeGen();
		}
		return timestamp;
	}

	/**
	 * 返回以毫秒为单位的当前时间
	 *
	 * @return 当前时间(毫秒)
	 */
	protected static long timeGen() {
		return System.currentTimeMillis();
	}

	@SuppressWarnings({ "unchecked", "rawtypes" })
	@Override
	public Long generateIdentifierOfType(TypeInformation type) {
		return nextId();
	}

	public static void main(String[] args) {
		System.out.println(System.currentTimeMillis());
		System.out.println(SnowflakeIdGenerator.nextId());
	}
}
